Log In  
BBS > Lexaloffle Community Superblog
This is a combined feed of all Lexaloffle user blogs. For Lexaloffle-related news, see @zep's blog.

All | Following | PICO-8 | Voxatron | General | Off-site
[ :: Read More :: ]

Cart #dashplus1-0 | 2024-04-25 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA

P#147307 2024-04-25 15:54 ( Edited 2024-04-25 15:54)
[ :: Read More :: ]

Greetings,

I'm relatively new to the Picotron and even though it is a "fantasy workstation" I was wondering if there was a way to use it as an actual OS specifically for the Raspberry Pi. If anyone knows if there is a way to use it as an ISO for a Raspberry Pi I would love to get some info as to learning how to make it it's own OS.

P#147303 2024-04-25 14:56
[ :: Read More :: ]

Cart #bad_rooms_ld55-2 | 2024-04-28 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA
3

Ludum Dare 55 entry

X/C cycle spells + Arrow keys to move
1-4 players vs 0-3 CPU players (max 4)

P#147290 2024-04-25 10:53 ( Edited 2024-04-28 13:53)
[ :: Read More :: ]

Cart #rebooter_-0 | 2024-04-24 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA
8

--REBOOTER--

Ordered by Elevated Recovery
Made by Charlie Boudchicha (https://www.fiverr.com/charlie_boud)

You need to complete every achievements to win the game :

  • make a bad decision
  • make a productive decision
  • do something to rest
  • unlock the 4 blue rooms
  • go through 15 rooms
  • survive 3 trap rooms
P#147277 2024-04-24 22:14 ( Edited 2024-05-01 11:32)
[ :: Read More :: ]

Hello, considering the following:

Trying to remake Vampire Survivors and a fun project
I have a character class
Characters have effects that are applied to a target. In this example my target = player
I have a character that is an instance of that class. Let's call him "Antonio"
I have a "run", Antonio is copied into run.player
I set player to be run.player

My problem is that I have to initiate the character class and Antonio before I create "player". But player is not yet created so player referenced on the character class is nil.

I hope this makes sense, I have a workaround but it feels clunky and I'm wondering if there are other solutions.

Here is my code before the workaround:

character = {}
character.__index = character
function character:new(name)
    local o = setmetatable({}, character)
    o.name = name
    o.x = 0
    o.y = 0
    o.sp_scale = 1
    o.w = 16
    o.h = 16
    o.effects = effects
    o.original_invincibility_frames = 10
    o.invincibility_frames = 0
    o.max_health = 100
    o.health = 20
    o.recovery = 0
    o.armor = 0
    o.move_speed = 1
    o.might = 1
    o.weapon_projectile_speed = 0
    o.weapon_duration = 0
    o.weapon_area = 0
    o.weapon_cooldown = 0
    o.weapon_projectiles_amount = 0
    o.revival = 0
    o.magnet = 0
    o.luck = 0
    o.growth = 0
    o.greed = 0
    o.curse = 0
    o.reroll = 0
    o.skip = 0
    o.banish = 0
    return o
end

function character:set_effects(effects)
    self.effects = effects
end

function character:apply_effects(level)

    local effects = self.effects
    for _, effect in pairs(effects) do
        if level % effect.level_increment == 0 and 
            level <= effect.max_level_increment then 
            local target = effect.target    
            if effect.mode == "percent" then
                target[effect.stat] += (target[effect.stat] or 0) * effect.value
            else
                target[effect.stat] = (target[effect.stat] or 0) + effect.value
            end
        end

    end
end

antonio = character:new("antonio")
antonio.recovery = 0.5
antonio:set_effects( {

                    {   description="Gains 10% Might every 2 Levels",  
                        stat = "might", 
                        value = 1,
                        target = player, -- here is the problem, player doesn't exist
                        level_increment = 2,
                        max_level_increment = 10,
                        mode = "percent"
                    },
                    {   description="Gains 0.5 Recovery every 1 level", 

                        stat = "recovery", 
                        value = 0.5,
                        target = player,
                        level_increment = 1,
                        max_level_increment = 5
                    }

                }

)

function _init()
    t = 0
    run = {}
    run.player = {}
    run.level = 1
    add(run.player, deepcopy(antonio))

    player = run.player[1]

end

function _update()

    if t % 60 == 0 then player.health += player.recovery end 
    if btnp(4) then
        run.level += 1
        player:apply_effects(run.level)
    end
    t += 1
end

function _draw()
    local y = 0
    cls(0)
    for entry in all(run.player) do
        for k, v in pairs(entry) do
            if type(v) == "number" then
                print(k..":"..v, 0, y)
                y += 8
            end
        end
    end
    local y = 0
    print("Level: "..run.level, 240, y)

end

function deepcopy(orig)
    local orig_type = type(orig)
    local copy
    if orig_type == 'table' then
        copy = {}
        for orig_key, orig_value in next, orig, nil do
            copy[deepcopy(orig_key)] = deepcopy(orig_value)
        end
        setmetatable(copy, getmetatable(orig))
    else -- for numbers, strings, booleans, etc
        copy = orig
    end
    return copy

end

And here is my code with the workaround, in short, once player is created I go and apply player as the target of the effects: player:update_effects_target(player)

character = {}
character.__index = character
function character:new(name)
    local o = setmetatable({}, character)
    o.name = name
    o.x = 0
    o.y = 0
    o.sp_scale = 1
    o.w = 16
    o.h = 16
    o.effects = effects
    o.original_invincibility_frames = 10
    o.invincibility_frames = 0
    o.max_health = 100
    o.health = 20
    o.recovery = 0
    o.armor = 0
    o.move_speed = 1
    o.might = 1
    o.weapon_projectile_speed = 0
    o.weapon_duration = 0
    o.weapon_area = 0
    o.weapon_cooldown = 0
    o.weapon_projectiles_amount = 0
    o.revival = 0
    o.magnet = 0
    o.luck = 0
    o.growth = 0
    o.greed = 0
    o.curse = 0
    o.reroll = 0
    o.skip = 0
    o.banish = 0
    return o
end

function character:set_effects(effects)
    self.effects = effects
end

function character:update_effects_target(target)
    for _, effect in pairs(self.effects) do
        effect.target = target
    end
end

function character:apply_effects(level)

    local effects = self.effects
    for _, effect in pairs(effects) do
        if level % effect.level_increment == 0 and 
            level <= effect.max_level_increment then 
            local target = effect.target    
            if effect.mode == "percent" then
                target[effect.stat] += (target[effect.stat] or 0) * effect.value
            else
                target[effect.stat] = (target[effect.stat] or 0) + effect.value
            end
        end

    end
end

antonio = character:new("antonio")
antonio.recovery = 0.5
antonio:set_effects( {

                    {   description="Gains 10% Might every 2 Levels",  
                        stat = "might", 
                        value = 1,
                        target = player, -- here is the problem, player doesn't exist
                        level_increment = 2,
                        max_level_increment = 10,
                        mode = "percent"
                    },
                    {   description="Gains 0.5 Recovery every 1 level", 

                        stat = "recovery", 
                        value = 0.5,
                        target = player,
                        level_increment = 1,
                        max_level_increment = 5
                    }

                }

)

function _init()
    t = 0
    run = {}
    run.player = {}
    run.level = 1
    add(run.player, deepcopy(antonio))

    player = run.player[1]
    player:update_effects_target(player)
end

function _update()

    if t % 60 == 0 then player.health += player.recovery end 
    if btnp(4) then
        run.level += 1
        player:apply_effects(run.level)
    end
    t += 1
end

function _draw()
    local y = 0
    cls(0)
    for entry in all(run.player) do
        for k, v in pairs(entry) do
            if type(v) == "number" then
                print(k..":"..v, 0, y)
                y += 8
            end
        end
    end
    local y = 0
    print("Level: "..run.level, 240, y)

end

function deepcopy(orig)
    local orig_type = type(orig)
    local copy
    if orig_type == 'table' then
        copy = {}
        for orig_key, orig_value in next, orig, nil do
            copy[deepcopy(orig_key)] = deepcopy(orig_value)
        end
        setmetatable(copy, getmetatable(orig))
    else -- for numbers, strings, booleans, etc
        copy = orig
    end
    return copy

end
P#147242 2024-04-24 10:32 ( Edited 2024-04-24 10:32)
[ :: Read More :: ]

I've been using this tutorial as my principal information for programming, since there's no picotron specific resource.

I'm experimenting with table-based classes, according to the guide I'm supposed to be able to create a base blueprint of an object and then instantiate it, but when I do so following the example, the object is not copied but instead it becomes a reference, because every change gets applied to the first object.

I made a sample project, first I try the guide's way, then I try it in a way I know works

enemies = {}
enemies2 = {}

enemy = {
    type = 0,
    sp = 1,
    x = 0,
    y = 0,
    dx = 0,
    dy = 0,
    update=function(self)
    self.x += self.dx
    self.y += self.dy  
  end,
    draw=function(self)
        spr(self.sp, self.x, self.y)
    end
}

goblin = enemy --copy enemy class
goblin.sp = 2
goblin.type = 3
goblin.x = 6
goblin.y = 10

ogre = enemy  --copy enemy class
ogre.sp = 3
ogre.type = 4
ogre.x = 40
ogre.y = 50

function _init()
add(enemies, enemy)
add(enemies, goblin)
add(enemies, ogre)

add_enemy(16,16,4)
add_enemy(32,32,5)
add_enemy(64,64,6)

end

function add_enemy(new_x,new_y,sprIndex)
    add(enemies2, {
    type = 0,
    sp = sprIndex,
    x = new_x,
    y = new_y,
    dx = 0,
    dy = 0,
    update=function(self)
        self.x += self.dx
        self.y += self.dy  
    end,
    draw=function(self)
        spr(self.sp, self.x, self.y)
    end
})
end

function _draw()
    for e in all(enemies) do
        e:draw()
    end

    for en in all(enemies2) do
        en:draw()
    end
end

If I define the object in the add function then each object acts as independent object, is this how tables are supposed to function?

Cart #tihuwubibu-0 | 2024-04-24 | Embed ▽ | License: CC4-BY-NC-SA
2

P#147234 2024-04-24 09:11
[ :: Read More :: ]

Cart #basic_platformer_mecanics-0 | 2024-04-24 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA
2

P#147232 2024-04-24 08:06
[ :: Read More :: ]

Cart #duwiramope-0 | 2024-04-24 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA
3


jus a little wip game

P#147225 2024-04-24 02:35
[ :: Read More :: ]

hi, i'm loving picotron so far! i just wanted to report some issues using coroutines.

when using coroutine.resume, it can sometimes return early when the coroutine runs for a while due to how picotron does timeslicing. from reading head.lua, i learned that using coresume instead of coroutine.resume fixes this, which makes me think maybe coroutine.resume should be replaced with it. another more minor issue is that coresume only returns the first value of a multiple-value return/yield from the coroutine, and not any more.

here's a simple test case:

local function long_running_coroutine ()
  for i = 1, 100 do
    cls()
  end
  return 1, 2
end

function working ()
  local c = coroutine.create (long_running_coroutine)
  coresume (c)
  assert (coroutine.status(c) == 'dead', 'this works')
end

function not_working ()
  local c = coroutine.create (long_running_coroutine)
  coroutine.resume (c)
  assert (coroutine.status(c) == 'dead', 'this doesn\'t work')
end

function also_not_working ()
  local c = coroutine.create (long_running_coroutine)
  local a, b, c = coresume (c)
  assert (a)
  assert (b)
  assert (c, 'only one coroutine return value returned')
end

function _draw ()
  working ()
  not_working ()
  also_not_working ()
end
P#147216 2024-04-23 22:47 ( Edited 2024-04-24 02:00)
[ :: Read More :: ]

Cart #pgui-5 | 2024-05-08 | Embed ▽ | License: CC4-BY-NC-SA
23

A GUI library for Picotron!

It includes basic components: boxes, text boxes, text input, buttons, sliders, radio and multiple selection buttons, and color selecter. And layout components: vertical and horizontal stacks, dropdowns, a topbar and a scrollable container.

It can be useful to test game mechanics or to build app interfaces.

You can use this cart and see how it works or check the documentation on the github repository.

(I'll try to make a video on how to use it and post it here too :))

Here's one simple example:

include "pgui.lua"

function _init()
  -- Define the initial value of the slider
    slidervalue = 10
end

function _update()
  -- Refresh pgui each frame
    pgui:refresh()

  -- Create a slider and set its value back from its return value
    slidervalue = pgui:component("hslider",{pos=vec(190,20),value=slidervalue})
end

function _draw()
    cls(5)

  -- Draw the circle, its size is based on the return value of the slider
    circfill(240,140,slidervalue,8)

   -- Draw all pgui components  
    pgui:draw()
end

Let me know if you use it for any projects or if you have improvement ideas!

Note: keyboard text input is not currently working on the online version

Changelog:

1.0.1:

  • Added layers option
  • Improved dropdown (now the content is shown one layer on top)
  • Minor performance improvements

1.0.2:

  • Fixed offset error in scrollable and fixed button response miscalculations inside scrollable
  • Added flr option to hslider
  • Fixed minor layout adjustments
  • Added sprite_box component based on @MaddoScientisto's suggestion
  • Fixed color palette not being passed to children components

1.0.3

  • Performance update, now components consume less cpu
  • Refactored codebase to make it cleaner and easier to expand
  • Minor display improvements in hslider
  • Fixed color palette change function

P#147208 2024-04-23 20:47 ( Edited 2024-05-08 17:52)
[ :: Read More :: ]

Cart #lens-3 | 2024-04-23 | Embed ▽ | License: CC4-BY-NC-SA
6

Lens 0.1.2

Capture and view debugging information. This app will run and allow you to send it values to log and inspect. Since this runs as a separate process, you can still inspect log values even in the event of your program crashing.

Supports all data types and includes the ability to inspect table values.

Usage

To use, include "libs/lens.lua" from the cart, and call Lens.log().

Otherwise just send a "log" event to the lens process:

send_message(lens_pid, {
    event = "log",
    value = "log output",
})

This is an early release to collect feedback, so please let me know if you find it useful or any issues you encounter.

P#147194 2024-04-23 14:46 ( Edited 2024-04-23 22:21)
[ :: Read More :: ]

Cart #dashlessplus-0 | 2024-04-23 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA

P#147190 2024-04-23 13:28
[ :: Read More :: ]

Cart #dualnback-0 | 2024-04-23 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA
3


n-back (Wikipedia, Gwern)

How to play:

1-Back: Remember whether the position or color (or both) is the same as in the previous round, press the according button/buttons if they match.

2-Back: Remember whether position or color are the same as 2 rounds ago.

N-back: Remember whether position or color are the same as n rounds ago.

Controls:

Z or <- for a position match

X or -> for a color match

P to pause (+ additional menu)

Tips:

Try playing in standard mode to understand how the game works, in endless the game ends when you make a mistake or miss a match.

You can switch colors for letters in the additional menu (P) in case you're color blind or are more comfortable recognizing them.

P#147180 2024-04-23 08:10
[ :: Read More :: ]

Cart #cascade_cascade-2 | 2024-04-27 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA
8

Cascade Cascade

A game somewhere in the space between a breakout clone and a puzzle bobble clone, inspired by a half-remembered game I played on an airplane seatback.

Controls

  • Aim with left and right arrows
  • Press [x] to fire

Or, as of 2024.04.27, point and click with the mouse to aim and fire.

Rules

This is an endless arcade game. Your goal is to survive by keeping the blocks from reaching the bottom.

  • Each hit reduces a block's strength by one. When a block's strength reaches zero, it's destroyed.
  • Powerups (white concentric circles) increase the number of balls you can accumulate for your next shot.
  • The number of balls you'll fire next shot is indicated by the combo meter on the right side of the screen. It increases by one-half for each block you hit.

Updates

2024.04.27

  • Updated collision detection to work a bit better on corner hits
  • Added mouse controls

2024.04.22

  • Initial post!
P#147174 2024-04-23 05:22 ( Edited 2024-04-27 19:47)
[ :: Read More :: ]

Cart #astroyd-2 | 2024-05-02 | Embed ▽ | License: CC4-BY-NC-SA
16

ASTROYD

Any good computer needs one of these. This is my version, with a bunch of juiceful extra stuff that wasn't in the original classic release.

What, I really have to explain the game? In this day and age? Fine...

The Controls

  • ⬅️ and ➡️ to steer.
  • ⬆️ to thrust forward.
  • [Z] to perform a hyperspace warp! Your momentum is conserved. You might warp face-first into an asteroid, though.
  • [X] to fire.

The Game

As you destroy asteroids and flying saucers, you will gain points. See, this is one of those "arcade games" where the points are the point, and the reward is a high score.

That's neat and all, but the game will also become more difficult as you increase your score and clear screens. I wonder if there's a limit...

Gain 10.000 points and you'll get an extra life, up to sixteen.

Some Advice

  • Try not to go into hyperspace unless you absolutely have to. It becomes more of a gamble as the number of asteroids increases.
  • Don't just sit there... The flying saucers will have a harder time aiming if you keep moving.
  • Destroying asteroids while you're right next to them is probably not going to end well for you when they split in two.
  • Remember that your own shots also wrap around the edges of the screen.

Also, Stuff

  • Aside from the main game there is also Lurker Mode. In this mode, there is only a single, small asteroid chunk, but flying saucers show up far more often, and they have much better aim. See how long you can take this...
  • You can go into the settings to enable the visualization of collision shapes. Also, something called "funky mode"...?

That's About It

If something seems off, it's probably my fault and you should yell at me about it. Have a good day!

It's pronounced "a-stroyed."

Changelog

1.2 (05/02/2024)

  • Added: A sound effect and screenshake are present when the cart first starts
  • Changed: The custom pause menu has been removed, as there is now one built into desktop Picotron
  • Fixed: Holding an option button for longer than one tick resulted in accidentally selecting "Play" or "try again"; there is now a small delay before menu actions will work when the game over screen comes up or the cart starts anew

1.1 (04/23/2024)

  • Added: High scores are tracked and shown both on the title screen and on game over
  • Fixed: Hyperspace warp affected player bullets

1.0 (04/23/2024)

  • Initial release
P#147169 2024-04-23 04:09 ( Edited 2024-05-02 10:54)
[ :: Read More :: ]

Cart #donarumobraingames-0 | 2024-04-23 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA
10


Welcome to Brain Games. A collection of puzzles and game to test our logic and puzzle-solving skills. Take your time and have fun in these 6 different games.

Jumps
Use peg solitaire rules to solve these puzzles. Use one chip to jump over another and remove the jumped chip from the board. The puzzles get more difficult as the levels get higher! Can you get the board down to one chip?

Dice
Can you get all the dice to the six face? These puzzles start out easy but increase in difficult as you level up. You'll need to use Rubik's Cube like logic to solve the more complex levels.

Math
Of course a straight-up math puzzle is included. Find the next number in the sequence. Some are easy some are more difficult. A calculator or a calculator like mind is recommended.

Lord
In this take on the classic Hammurabi (or Hamurabi) game, you'll take control of a land and it's people for a ten year reign if you can make it that long. Control who is fed and who starves! Can you make it out with your head intact?

Bulls
The classic logic game of Bulls & Cows. Try to guess a four-digit, non-repeating number using clues. A bull for correct numbers in the right position and a cow for correct numbers in the wrong position.

Lights
Another classic. Try to turn off all the lights. These puzzles get hard as the levels progress. Can you find the right patterns to win every time?

P#147167 2024-04-23 02:24
[ :: Read More :: ]

Mate-in-2 Volume 2. 33 more puzzles. White to move.

Cart #matein2vol2-0 | 2024-04-22 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA
2

P#147158 2024-04-22 23:58
[ :: Read More :: ]

Cart #cgol-2 | 2024-04-23 | Embed ▽ | License: CC4-BY-NC-SA
4

Here is Conway's Game of Life in Picotron, using the colour tables as a fast way to count all the pixels at once.
This version does not work in the web player, because input is not detected ( @zep pls fix!)

Controls:
Up: Increase simulation speed
Down: Decrease simulation speed
Space: Pause
F: Step one frame
C: Clear screen
R: Randomise screen
L-Click: Draw pixels
R-Click: Erase Pixels
L: Toggle large cursor

Basic code:

function _init()
    frame = 0

    -- Set up userdata so we can draw the screen to itself
    -- by using memcpy() to a memmapped region of memory.
    -- The userdata is now the contents of the previous frame,
    -- and it is now possible to modify the current frame.
    -- We can also call spr() with userdata
    -- to draw the previous frame to the current one

    screen = userdata("u8", 480, 270)
    memmap(0x30000, screen)
end

function set_col_table(new, current, col)
    poke(0x8000 + 64*new + current, col)
end

function _draw()
    frame += 1

    if frame == 1 then
        -- Randomise the screen for the first frame.
        for y=0,269 do
            for x=0,479 do
                if (rnd() < 0.2) pset(x, y, 7)
            end
        end

        -- Copy the current screen to the buffer, just for the first frame
        memcpy(0x30000, 0x10000, 0x20000)

    end

    --if (frame % 32 > 0) return

    cls()

    -- White cells drawn onto colour 0 will set the colour to 1, white onto 1 will be 2
    -- and so on. This counts the number of neighbouring cells very quickly
    for i=0, 7 do
        set_col_table(7, i, i+1)
    end

    -- Draw the screen 8 times in a ring, for each neighbour. The colour tables
    -- do the counting
    for y=-1,1 do
        for x=-1,1 do
            if (x!=0 or y!=0) spr(screen, x, y)
        end
    end

    -- Set up colour tables to turn the "counted" screen into the next frame
    -- Set every colour to black except for drawing black onto 3 (brought back alive)
    -- and 2 or 3 neighbours for alive (stay alive)
    for i=0, 9 do
        set_col_table(0, i, 0)
        set_col_table(7, i, 0)
    end
    set_col_table(0, 3, 7)
    set_col_table(7, 2, 7)
    set_col_table(7, 3, 7)

    -- Draw the screen to the "counted" version, with the rules set above
    spr(screen, 0, 0)

    -- Reset the draw state to make sure we have a predictable next frame
    reset()

    -- Copy the screen to the buffer for the next frame,
    -- before we pollute it with the FPS counter
    memcpy(0x30000, 0x10000, 0x20000)

    -- FPS counter
    if key("x") then
        rectfill(0, 0, 44, 8, 0)
        print("FPS: "..stat(7), 1, 1, 8)
    end

end

P#147143 2024-04-22 21:15 ( Edited 2024-04-23 21:37)
[ :: Read More :: ]

Cart #flapperduck-0 | 2024-04-22 | Embed ▽ | License: CC4-BY-NC-SA
3

hey people,
this is flapperDuck,
a flappy bird game that I made after watching enough tutorials on Picotron to feel comfortable coding a game in it, for whatever reason I decided not to look up or actually play flappy bird before making this so this is all from memory and I'm sure not everything is the same, in fact other than the gameplay I'm guessing its quite different

anyway-
keyboard controls are:

z - jump/select
arrows - selecting on the menu
(space also works for jumping but not in browser version)

controller controls are (Xbox controller):

A - select
A/B/X/Y - jump
d-pad - selecting on the menu

the last 2 skins are unlocked when you reach a high score of 8000
and if you (like me) really like the look of old original Gameboy games you might like one of the unlockable skins
as you can see in the screenshot my high score is 12874
feel free to post your high score in the description and have fun

-playerMan

P#147124 2024-04-22 17:04 ( Edited 2024-04-22 19:20)
[ :: Read More :: ]

Cart #stellae-0 | 2024-04-22 | Code ▽ | Embed ▽ | No License

This is a Space Invaders clone that I was making some time last year. Maybe I'll continue with the project.

P#147129 2024-04-22 16:50
View Older Posts